home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
scope
/
051-075
/
scopedisk57
/
expand
/
main.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-19
|
8KB
|
252 lines
/*
* M A I N . C
*
* A file maintenance utility to decompress files; compatible with the
* public domain COMPRESS v4.0 utility by Spencer W. Thomas, et al.
*
* Algorithm from "A Technique for High Performance Data Compression",
* Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
*
* The impetus for this program comes from a desire to decompress files
* in an MS-DOS environment with 16-bit codes using fewer machine
* resources than the public domain COMPRESS utility. Memory usage
* for MS-DOS is less than 250 kbytes for 16-bit codes.
*
* The code uses many ANSI-C style contructs with no apologies.
*-----------------------------------------------------------------------
* Authors: Lyle V. Rains
* based on the source of COMPRESS v4.0 by Spencer Thomas, et al.
*-----------------------------------------------------------------------
* Revision History:
*/
#define MAIN
#include "main.h"
char *myname = MYNAME;
static char *stdinname = STDIN;
static char *stdoutname = STDOUT;
/* Magic 2-byte file header */
static char header[] = {0x1F, 0x9D};
static char xbuf[XBUFSIZE]; /* Big buffers for setvbuf()*/
static char zbuf[ZBUFSIZE];
static void usage(all)
int all;
{
U(" Usage: %s [%ch(elp)] [%ccko] [%cf] [files ...]\n", myname, SW, SW, SW);
if (!all) return;
#ifdef __DATE__
U("\n Version %s, %s\n", VERSION, __DATE__);
#else
U("\n Version %s\n", VERSION);
#endif
U(" Decompresses files in a method compatible with COMPRESS V4.0\n");
U(" INITIAL PUBLIC RELEASE. Please send feedback and bug reports to:\n");
U(" Lyle Rains, Compuserve User ID 71250,324\n");
U("\n Switches:\n");
U(" %ch this help message.\n", SW);
U(" %cc concatenate output to stdout.\n", SW);
#if KILL == YES
U(" %ck keep input file (default: delete).\n", SW);
#else
U(" %ck kill input file (default: keep).\n", SW);
#endif
U(" %co overwrite pre-existing output file.\n", SW);
U(" %cf filter (%s stdin to stdout; ignore additional args).\n", SW, myname);
#if DEBUGOUT
U(" %cd print debug info to stderr.\n", SW);
#endif
U(" File arguments:\n");
U(" infile %s infile (e.g., FOO.C%s) to default outfile (FOO.C)\n", myname, ZSUFFIX);
U(" outfile %s to outfile (e.g., FOO.C) from default infile (FOO.C%s)\n", myname, ZSUFFIX);
U(" in%cout %s infile to outfile (overrides %cc and defaults).\n", TO, myname, SW);
return;
}
static int do_expand();
main(argc, argv)
int argc;
char *argv[];
{
char **nextarg;
int argsleft;
char *arg, *out;
int kill, overwrite, cat;
char name_tmp[NAMEBUFSIZ];
FILE *infile, *outfile;
char userinput[2];
if ((argsleft = argc) <= 1) {
# if FILTER == YES
do_expand(stdin, stdout, stdinname, stdoutname); exit (NORMAL);
# else
/* No arguments -- give short usage message and exit with error */
usage(QUICK); exit (ERROR);
# endif
}
kill = KILL;
cat = NO;
overwrite = NO;
/* Process command line arguments */
nextarg = argv;
while (--argsleft > 0) {
if (*(arg = *++nextarg) == SW) {
/* Process command switches */
while (*++arg) {
switch (*arg) {
case 'h':
case 'H':
case '?':
usage(ALL); exit (NORMAL);
case 'f':
case 'F':
do_expand(stdin, stdout, stdinname, stdoutname); exit (NORMAL);
case 'c':
case 'C':
cat = !cat; break;
case 'o':
case 'O':
overwrite = !overwrite; break;
case 'k':
case 'K':
kill = !kill; break;
default:
eprintf("%s: bad switch '-%c'.\n", myname, *arg);
usage(QUICK);
exit (ERROR);
# if DEBUGOUT
case 'd':
case 'D':
debug = !debug; break;
# endif
}
}
}
else if (*arg == TO) {
/* Inappropriate place to name output file. Find file name. */
if (!*++arg) arg = --argsleft ? *++nextarg : "<noname>";
errexit(("%s: no input file for output '=%s'.\n", myname, arg), ERROR);
}
else {
/* Get input/output filespecs and open them for processing.
* Note: filename processing is necessarily system dependent.
* I am providing suitable routines for MS-DOS, but you must
* supply your own routines as necessary for other systems.
*
* First check to see if there is an output file spec. If the
* output prefix character isn't in the current argument, check
* the beginning of the next arg, in case the user added whitespace.
*/
if ( (out = strchr(arg, TO)) != 0
|| ( (argsleft > 1)
&& (out = (**(nextarg + 1) == TO) ? (--argsleft, *++nextarg) : NULL) != 0
)
)
{
/* We found the prefix character. Now find the output
* filespec, again allowing for the possible inclusion of
* whitespace after the prefix character.
*/
*out = '\0';
if ( !*++out && (out = --argsleft ? *++nextarg : NULL) == 0 ) {
errexit(("%s: missing output filespec for '%s='.\n", myname, arg),ERROR);
}
}
else if (cat) out = stdoutname;
else {
/* Create a default output filespec, trying the following:
* 1. If the input filespec file extension ends with ZSUFFIX,
* then default output spec is the same as the input, with
* ZSUFFIX removed.
* 2. If the input doesn't end in ZSUFFIX, assume that the
* user is really giving the desired output spec, and try to
* create a default input spec by appending ZSUFFIX to
* the filespec and seeing if any such input file exists.
* 3. Give up (we really tried!), print an error and continue.
*/
out = strcpy(name_tmp, arg);
if (!to_xname(out)) {
out = arg;
arg = name_tmp;
to_zname(arg);
}
}
if (!overwrite && out != stdoutname && (outfile = fopen(out, "r")) != 0) {
eprintf("%s: output '%s' exists. Overwrite? ", myname, out);
if (fgets(userinput, 2, stdin) && tolower(*userinput) == 'y')
fclose(outfile);
else
exit (ERROR);
}
if ((outfile = (out == stdoutname) ? (FILE *)stdout : fopen(out, "w")) == 0) {
errexit(("%s: can't open output '%s'.\n", myname, out), ERROR);
}
if ((infile = fopen(arg, "r")) == 0) {
errexit(("%s: can't open input '%s'.\n", myname, arg), ERROR);
}
eprintf("expanding '%s' to '%s'.\n", arg, out);
DBG(("\t overwrite = %d, cat = %d, kill = %d\n", overwrite, cat, kill));
do_expand(infile, outfile, arg, out);
if (kill) unlink(arg);
}
}
}
static int do_expand(in, out, inname, outname)
FILE *in;
FILE *out;
char *inname;
char *outname;
{
int maxbits;
int clearmode;
setvbuf(in, zbuf, _IOFBF, ZBUFSIZE);
setvbuf(out, xbuf, _IOFBF, XBUFSIZE);
setbinary(in); /* Some systems require this */
setbinary(out);
/* Check for the magic number header */
if ((char)getc(in) != header[0] || (char)getc(in) != header[1]) {
errexit(("%s: '%s' not in compressed format.\n", myname, inname), ERROR);
}
maxbits = getc(in);
clearmode = maxbits & BLOCK_MASK;
if ((maxbits &= BIT_MASK) > Maxbits) {
errexit(("%s: '%s' uses %d-bit compression (%s allows %d-bits maximum).\n",
myname, inname, maxbits, myname, Maxbits), ERROR);
}
switch (expand(in, out, maxbits, clearmode)) {
case OK:
fclose(in);
if (out != stdout) fclose(out);
else fflush(out);
return (OK);
case NOMEM:
errexit(("%s: not enough memory to expand '%s'.\n", myname, inname), ERROR);
case TOKTOOBIG:
errexit(("%s: token too long in '%s'.\n", myname, inname), ERROR);
case READERR:
errexit(("%s: read error on input '%s'.\n", myname, inname), ERROR);
case WRITEERR:
errexit(("%s: write error on output '%s'.\n", myname, outname), ERROR);
case CODEBAD:
errexit(("%s: illegal code found in '%s'.\n", myname, inname), ERROR);
case TABLEBAD:
errexit(("%s: internal error -- tables corrupted.\n", myname), ERROR);
default:
errexit(("%s: internal error -- illegal return value.\n", myname), ERROR);
}
}